﻿// <author>Netmon Cipher Dev Team</author>
// <date>2009-07-16</date>
// <version>1.0</version>
// <summary>This contains the entry point Main function.</summary>

namespace SSLDecryptionExpert
{
    using System;
    using System.Runtime.InteropServices;
    using Microsoft.NetworkMonitor;

    /// <summary>
    /// SSL Decryption Class
    /// </summary>
    public partial class SSLDecryption
    {
        #region PaserCallBack

        /// <summary>
        /// This Method is Called when the Parser Engine has information or an error message
        /// </summary>
        /// <param name="callerContext">Called Context given to the Parsing Engine</param>
        /// <param name="statusCode">Message Status Code</param>
        /// <param name="description">Description Text of Error</param>
        /// <param name="type">Type of Message</param>
        public static void ParserCallback(
                                    IntPtr callerContext,
                                    uint statusCode,
                                    string description,
                                    NmCallbackMsgType type)
        {
            if (type == NmCallbackMsgType.Error)
            {
                Variables.Log.LogMessage("ERROR: " + description);
            }
            else
            {
                Variables.Log.LogMessage(description);
            }
        }

        #endregion

        #region Initialize NMAPI

        /// <summary>
        /// Takes care of initializing the Network Monitor API
        /// </summary>
        /// <returns>true on success</returns>
        private bool InitializeNMAPI()
        {
            try
            {
                // Initialize the NMAPI          
                NM_API_CONFIGURATION apiConfig = new NM_API_CONFIGURATION();
                apiConfig.Size = (ushort)Marshal.SizeOf(apiConfig);
                apiConfig.ReassemblyOption = NmReassemblyConfigOption.None;                

                ulong errorValue = NetmonAPI.NmGetApiConfiguration(ref apiConfig);
                if (errorValue != (ulong)Status.ERROR_SUCCESS)
                {
                    Variables.Log.LogMessage("There was an error loading NMAPI Configuration.");
                    return false;
                }

                errorValue = NetmonAPI.NmApiInitialize(ref apiConfig);
                if (errorValue != (ulong)Status.ERROR_SUCCESS)
                {
                    Variables.Log.LogMessage("There was an error Initializing NMAPIs");
                    return false;
                }

                Variables.Log.LogMessage("NMAPIs Initialized.");
                return true;
            }
            catch (BadImageFormatException)
            {
                Variables.Log.LogMessage(
                            "There was an error loading the NMAPI." + 
                            " Please ensure you have the correct" +
                            " version installed for your platform.");

                return false;
            }
            catch (DllNotFoundException)
            {
                Variables.Log.LogMessage(
                                "There was an error loading the NMAPI." +
                                " Please ensure you have the correct" +
                                " version installed for your platform.");
                return false;
            }
        }

        #endregion

        #region Load NM Parsers

        /// <summary>
        /// Call to Load Network Monitor Parsers.
        /// </summary>
        /// <param name="conversationFilter">Conversation Filter</param>
        /// <param name="nplParser">Parser handle</param>
        /// <param name="frameParser">Frame Handle</param>
        /// <param name="frameParserConfig">
        /// Frame Parser Config Handle.</param>
        /// <returns>
        /// Returns TRUE if Parsers are loaded successfully.
        /// </returns>
        private bool LoadParsers(
                            string conversationFilter,
                            out IntPtr nplParser,
                            out IntPtr frameParser,
                            out IntPtr frameParserConfig)
        {
            uint errorValue = 0;
            nplParser = IntPtr.Zero;
            frameParser = IntPtr.Zero;
            frameParserConfig = IntPtr.Zero;          

            Variables.Log.LogMessage("Initializing Netmon Parsers...");

            try
            {
                //// Passing in null for the path will use the default configuration
                //// as specified in the Netmon UI            
                errorValue = NetmonAPI.NmLoadNplParser(
                                null,
                                NmNplParserLoadingOption.NmAppendRegisteredNplSets,
                                errorCallback,
                                IntPtr.Zero,
                                out nplParser);
            }
            catch (BadImageFormatException)
            {
                Variables.Log.LogMessage("Loading of Netmon Parsers failed.  Wrong API Version.");
            }
            catch (DllNotFoundException)
            {
                Variables.Log.LogMessage("Loading of Netmon Parsers failed.  No API Found");
            }
            catch
            {
                
                return false;
            }

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Initialization of Netmon Parsers failed.");
                return false;
            }

            Variables.Log.LogMessage("Netmon Parsers initialized successfully.");

            // Configure Frame Parser
            errorValue = NetmonAPI.NmCreateFrameParserConfiguration(
                                nplParser,
                                errorCallback,
                                IntPtr.Zero,
                                out frameParserConfig);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Creation of frame parser configuration has failed");
                return false;
            }

            bool loadedparserengine = true;

            // Enable Conversations
            errorValue = NetmonAPI.NmConfigConversation(
                            frameParserConfig,
                            NmConversationConfigOption.None,
                            true);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Unable to configure conversation options");
                return false;
            }

            // "Enable Reassembly...."
            errorValue = NetmonAPI.NmConfigReassembly(
                           frameParserConfig,
                           NmReassemblyConfigOption.None,
                           true);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Unable to configure conversation options");
                return false;
            }

            ////"Adding Display Filter..."
            Variables.Log.LogMessage(
                            "Adding " 
                            + Constants.DisplayFilter 
                            + " Display Filter...");

            if (NetmonAPI.NmAddFilter(
                                frameParserConfig,
                                Constants.DisplayFilter,
                                out this.displayFilterId) == (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage(Constants.DisplayFilter + " Display Filter added successfully");

                if (!string.IsNullOrEmpty(conversationFilter))
                {
                    ////"Adding Conversation Filter..."
                    Variables.Log.LogMessage(
                                    "Adding "
                                    + conversationFilter
                                    + " Conversation Filter...");

                    if (conversationFilter.Substring(0, 16).ToLower() != "conversation.tcp")
                    {
                        Variables.Log.LogMessage(
                                    "****Warning****: Using a non TCP Conversation Filter, "
                                    + conversationFilter + ", might cause the expert to fail.  "
                                    + "You should use a filter at the TCP layer or higher.  "
                                    + "A conversation filter at a higher level might work, say IPv4 or IPv6, but this depends on the traffic.  "
                                    + "Under these conditions all traffic must use the same certificate and the traffic for each conversation must be sequential.");
                    }

                    if (NetmonAPI.NmAddFilter(
                                    frameParserConfig,
                                    conversationFilter,
                                    out this.conversationFilterId) == (ulong)Status.ERROR_SUCCESS)
                    {
                        Variables.Log.LogMessage("Conversation Filter, " + conversationFilter + " added successfully");
                    }
                    else
                    {
                        Variables.Log.LogMessage(
                                "Error occurred while adding Conversation filter "
                                + conversationFilter);
                    }
                }

                // Add filter for checking if SSL is used.
                if (NetmonAPI.NmAddFilter(
                                frameParserConfig,
                                Constants.SSLVersion,
                                out this.SSLVersionId) == (ulong)Status.ERROR_SUCCESS)
                {
                    Variables.Log.LogMessage("SSL Version Filter added successfully");
                }
                else
                {
                    Variables.Log.LogMessage(
                            "Error occurred while adding Conversation filter "
                            + conversationFilter);
                }
            }
            else
            {
                Variables.Log.LogMessage(
                                "Error occurred while adding Display filter " 
                                + Constants.DisplayFilter);
                return false;
            }

            ////"Creating Frame parser..."
            errorValue = NetmonAPI.NmCreateFrameParser(
                            frameParserConfig,
                            out frameParser,
                            NmFrameParserOptimizeOption.ParserOptimizeNone);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage(
                            "This configuration is not supported for" +
                            " creating frame parser");
                return false;
            }

            if (!loadedparserengine)
            {
                NetmonAPI.NmCloseHandle(frameParserConfig);
                NetmonAPI.NmCloseHandle(nplParser);
            }

            return loadedparserengine;
        }

        /// <summary>
        /// Call to Load Network Monitor Parsers.
        /// </summary>
        /// <param name="conversationFilter">Conversation Filter</param>
        /// <param name="nplParser">Parser handle</param>
        /// <param name="frameParser">Frame Handle</param>
        /// <param name="frameParserConfig">
        /// Frame Parser Config Handle.</param>
        /// <returns>
        /// Returns TRUE if Parsers are loaded successfully.
        /// </returns>
        private bool LoadEvalParser(
                            string conversationFilter,
                            IntPtr nplParser,
                            out IntPtr frameParser,
                            out IntPtr frameParserConfig)
        {
            uint errorValue = 0;
            frameParser = IntPtr.Zero;
            frameParserConfig = IntPtr.Zero;
            
            // Configure Frame Parser
            errorValue = NetmonAPI.NmCreateFrameParserConfiguration(
                                nplParser,
                                errorCallback,
                                IntPtr.Zero,
                                out frameParserConfig);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Creation of frame parser configuration has failed");
                return false;
            }

            bool loadedparserengine = true;

            // Enable Conversations
            errorValue = NetmonAPI.NmConfigConversation(
                            frameParserConfig,
                            NmConversationConfigOption.None,
                            true);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Unable to configure conversation options");
                return false;
            }

            // "Enable Reassembly...."
            errorValue = NetmonAPI.NmConfigReassembly(
                           frameParserConfig,
                           NmReassemblyConfigOption.None,
                           true);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Unable to configure conversation options");
                return false;
            }

            if (!string.IsNullOrEmpty(conversationFilter))
            {
                ////"Adding Conversation Filter..."
                Variables.Log.LogMessage(
                                "Adding "
                                + conversationFilter
                                + " Conversation Filter...");

                if (conversationFilter.Substring(0, 16).ToLower() != "conversation.tcp")
                {
                    Variables.Log.LogMessage(
                                "****Warning****: Using a non TCP Conversation Filter, "
                                + conversationFilter + ", might cause the expert to fail.  "
                                + "You must use a filter at the TCP layer or higher.  "
                                + "A conversation filter at a higher level might work, say IPv4 or IPv6, but this depends on the traffic.  "
                                + "Under these conditions all traffic must use the same certificate and the traffic for each conversation must be sequential.");
                }

                if (NetmonAPI.NmAddFilter(
                                frameParserConfig,
                                conversationFilter,
                                out this.evalConversationFilterId) == (ulong)Status.ERROR_SUCCESS)
                {
                    Variables.Log.LogMessage("Eval Parser Conversation Filter, " + conversationFilter + " added successfully");
                }
                else
                {
                    Variables.Log.LogMessage(
                            "Error occurred while adding Conversation filter "
                            + conversationFilter);
                }
            }
            else
            {
                Variables.Log.LogMessage(
                        "****Warning, no Conversation filter added.");
            }

            ////"Creating Frame parser..."
            errorValue = NetmonAPI.NmCreateFrameParser(
                            frameParserConfig,
                            out frameParser,
                            NmFrameParserOptimizeOption.ParserOptimizeNone);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage(
                            "This configuration is not supported for" +
                            " creating frame parser");
                return false;
            }

            if (!loadedparserengine)
            {
                NetmonAPI.NmCloseHandle(frameParserConfig);
            }

            return loadedparserengine;
        }

        #endregion

        #region Open Capture File

        /// <summary>
        /// Opens the Capture file to read.
        /// </summary>
        /// <param name="fileName">File name to open</param>
        /// <param name="captureFileHandle">Handle to Capture File.</param>
        /// <returns>Returns the status of operation.</returns>
        private bool OpenCaptureFile(string fileName, out IntPtr captureFileHandle)
        {
            captureFileHandle = IntPtr.Zero;

            Variables.Log.LogMessage(
                        "Opening Encrypted Capture File: " +
                        fileName);

            ulong errorValue = NetmonAPI.NmOpenCaptureFile(
                                fileName,
                                out captureFileHandle);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage(
                                "Unable to open encrypted capture file " +
                                fileName +
                                "\n\n Either file does not exist or it is already in use");                
                return false;
            }

            return true;
        }

        #endregion

        #region Create Capture File

        /// <summary>
        /// Creates Capture File.
        /// </summary>
        /// <param name="fileName">Output file name.</param>
        /// <param name="createdFileHandle">Handle to created File.</param>
        /// <returns>Returns true if creation is successful.</returns>
        private bool CreateCaptureFile(string fileName, out IntPtr createdFileHandle)
        {
            createdFileHandle = IntPtr.Zero;

            Variables.Log.LogMessage(
                        "Creating Decrypted Capture File: " +
                        fileName);

            uint capSize;
            ulong errorValue = NetmonAPI.NmCreateCaptureFile(
                                        fileName,
                                        2147483648,
                                        NmCaptureFileFlag.Chain,
                                        out createdFileHandle,
                                        out capSize);

            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage(
                    "Error occurred while creating Decrypted Capture File: " +
                    fileName);
                return false;
            }

            return true;
        }

        #endregion

        #region ParsedFrameInformation

        /// <summary>
        /// To retrieve information about a specific frame
        /// </summary>
        /// <param name="parsedFrame">Parsed Frame</param>
        /// <param name="frameNumber">Frame Number</param>
        /// <param name="isKeyBlockComputed">TRUE if KeyBlock is computed</param>
        /// <param name="decryptedAppDataPacket">
        /// Checks if packet is Application Data.
        /// </param>
        /// <param name="exitOnError">True if we should close the Expert on an Error</param>
        /// <param name="filter">Object of Filter Set</param>
        /// <returns>Returns TRUE if Frame is processed correctly</returns>
        private bool ParsedFrameInformation(
                                    IntPtr parsedFrame,
                                    ref uint frameNumber,
                                    ref bool isKeyBlockComputed,
                                    out bool decryptedAppDataPacket,
                                    ref bool exitOnError,
                                    NMFilters filter)
        {
            Variables.Log.LogMessage("\n\n===========================================================================");
            Variables.Log.LogMessage("Processing Frame Number: " + (frameNumber + 1).ToString());
            Variables.Log.LogMessage("===========================================================================");
            uint errorValue;
            string fieldName = string.Empty;
            string fieldValue = string.Empty;
            string sourceIP = string.Empty;
            string destinationIP = string.Empty;
            string nextfilterToSearch = string.Empty;            
            decryptedAppDataPacket = false;
            bool decryptionResult = true;
            Variables.multipleApplicationData = new byte[0];

            uint fieldCount;
            errorValue = NetmonAPI.NmGetFieldCount(parsedFrame, out fieldCount);
            if (errorValue != (uint)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Error: Found No Fields!");
                return false;
            }

            Variables.Log.LogMessage("Found " + fieldCount + " Fields in Frame");

            for (uint fid = 0; fid < fieldCount; fid++)
            {
                if (fid == 0 && Variables.previousFrameNumber == frameNumber)
                {
                    fid = Variables.nextFid;
                    if (Variables.isSourceClient)
                    {
                        sourceIP = Variables.clientIP;

                        Variables.Log.LogMessage("Using Client Source IP " + sourceIP);
                    }
                    else
                    {
                        sourceIP = Variables.serverIP;

                        Variables.Log.LogMessage("Using Server Source IP " + sourceIP);
                    }

                    continue;
                }

                bool res = this.GetFieldName(parsedFrame, fid, out fieldName);
                if (!res)
                {
                    Variables.Log.LogMessage("Error: Couldn't get Field Name");
                    continue;
                }

                res = this.GetFieldValueFromUI(parsedFrame, fid, out fieldValue);
                if (!res)
                {
                    Variables.Log.LogMessage("Error: Couldn't get Field Value for " + fieldName);
                    continue;
                }

                Variables.Log.LogMessage((frameNumber+1).ToString() + "," + fid + ": Processing Field: " + fieldName + "\n   Value: " + fieldValue);

                //// Field Value
                int[] value = this.ParsedFrameFieldValue(parsedFrame, fid);

                if (fieldName.EndsWith(".SourceAddress", StringComparison.OrdinalIgnoreCase))
                {
                    sourceIP = fieldValue;

                    Variables.Log.LogMessage("Repurposing Source IP Address: " + sourceIP);
                }

                if (fieldName.EndsWith(".DestinationAddress", StringComparison.OrdinalIgnoreCase))
                {
                    destinationIP = fieldValue;

                    Variables.Log.LogMessage("Repurposing Destination IP Address " + destinationIP);
                }



                if (fieldName.EndsWith("Tcp.SrcPort", StringComparison.OrdinalIgnoreCase) ||
                    fieldName.EndsWith("TCPProperties.SourcePort", StringComparison.OrdinalIgnoreCase))
                {
                    ulong sourcePortValue = 0;
                    if (value.Length == 1)
                    {
                        sourcePortValue = (ulong)value[0];
                        Variables.sourcePort = BitConverter.GetBytes(sourcePortValue);
                        Array.Reverse(Variables.sourcePort);
                    }
                    else if (value.Length == 8)
                    {
                        byte[] arr = new byte[value.Length];
                        for (int i = 0; i < value.Length; i++)
                        {
                            arr[i] = (byte)value[i];
                        }

                        sourcePortValue = BitConverter.ToUInt64(arr, 0);
                        Variables.sourcePort = BitConverter.GetBytes(sourcePortValue);                        
                    }

                    Variables.Log.LogMessage("Using Source Port: " + sourcePortValue);
                }

                if (fieldName.EndsWith("Tcp.DstPort", StringComparison.OrdinalIgnoreCase) ||
                    fieldName.EndsWith("TCPProperties.DestinationPort", StringComparison.OrdinalIgnoreCase))
                {
                    ulong destinationPortValue = 0;
                    if (value.Length == 1)
                    {
                        destinationPortValue = (ulong)value[0];
                        Variables.destinationPort = BitConverter.GetBytes(destinationPortValue);
                        Array.Reverse(Variables.destinationPort);
                    }
                    else if (value.Length == 8)
                    {
                        byte[] arr = new byte[value.Length];
                        for (int i = 0; i < value.Length; i++)
                        {
                            arr[i] = (byte)value[i];
                        }

                        destinationPortValue = BitConverter.ToUInt64(arr, 0);
                        Variables.destinationPort = BitConverter.GetBytes(destinationPortValue);                        
                    }

                    Variables.Log.LogMessage("Using Destination Port: " + destinationPortValue);

                    // If source/dest IP are the same, then it's local traffic.  Tack on IP to differentiate.
                    if (sourceIP == destinationIP)
                    {
                        sourceIP += string.Format("{0}", BitConverter.ToString(Variables.sourcePort));
                        destinationIP += string.Format("{0}", BitConverter.ToString(Variables.destinationPort));
                    }
                }

                if (fieldName.Equals(filter.ContentTypeFilter, StringComparison.OrdinalIgnoreCase))
                {
                    Variables.contentType = (byte)value[0];

                    // Save TCP Payload Offset in case we need it later.
                    uint FieldSize;
                    NetmonAPI.NmGetFieldOffsetAndSize(parsedFrame, fid, out Variables.TCPPayloadOffset, out FieldSize);

                    Variables.Log.LogMessage("Found Content Type: " + Variables.contentType + " (" + filter.ContentTypeFilter + ")");
                }

                if (fieldName.Equals(filter.ClientSessionIdFilter, StringComparison.OrdinalIgnoreCase))
                {
                    Variables.Log.LogMessage("Found Client Session IDs " + value.Length + " (" + filter.ClientSessionIdFilter + ")");
                    Variables.clientSessionID = new byte[value.Length];
                    for (int index = 0; index < Variables.clientSessionID.Length; index++)
                    {
                        Variables.clientSessionID[index] = (byte)value[index];

                        Variables.Log.LogMessage("Added Client Session ID: " + value[index].ToString());
                    }

                    // If the last session ID matches, then we can try to decrypt using that information.
                    // But if it doesn't match, then we'll have to return an error.
                    if (Variables.serverSessionID != null)
                    {
                        for (int index = 0; index < Variables.clientSessionID.Length; index++)
                        {
                            if (Variables.clientSessionID[index] != Variables.serverSessionID[index])
                            {
                                Variables.Log.LogMessage("Session ID Length != 0 and it doesn't match the last ServerHello Session ID.  We can't decrypt traces with multiple different keys.");

                                Variables.Log.DisplayException("The Initial ClientHello contains a reused Session ID.  We can't decrypt traces with multiple sessions unless the first session does a full SSL negotiation.  "
                                                                    + "Session ID Length in the ClientHello must be zero or contain the original Session ID from a previous Server Hello.  "
                                                                    + "You can try to restart the application that is generating the secure connection or "
                                                                    + "narrow down the trace so it contains only one Session ID.");

                                return false;
                            }
                        }
                    }
                    else
                    {
                        Variables.Log.LogMessage("Session ID Length != 0 and there is no previous full TLS/SSL session setup.  We can't decrypt traces without at least one full session setup.  ");

                        Variables.Log.DisplayException("ClientHello contains a Reused Session ID and the intial session setup is missing.  "
                                                            + "Session ID Length in the first ClientHello must be zero.  "
                                                            + "You can try to restart the application that is generating the secure connection or "
                                                            + "narrow down the trace so it contains only one Session ID.");

                        return false;
                    }
                }

                // Check and see if the session ID length is zero, if not and this is
                // in the Client Hello state, then we can't decrypt this stream.
                if (fieldName.Equals(filter.ClientSessionLengthIdFilter, StringComparison.OrdinalIgnoreCase)
                    && currentState == CurrentState.ClientHelloDone
                    && value[0] != 0)
                {
                    Variables.Log.LogMessage("Warning: Session ID Length != 0, we can't decrypt sessions with multiple different reused keys."
                                                + "If the ServerHello Session ID doens't match the last encountered ClientHello Session ID the decryption will fail.");
                }

                if (fieldName.Equals(filter.ServerSessionIdFilter, StringComparison.OrdinalIgnoreCase))
                {
                    Variables.Log.LogMessage("Found Server Session IDs " + value.Length + " (" + filter.ServerSessionIdFilter + ")");
                    Variables.serverSessionID = new byte[value.Length];
                    for (int index = 0; index < Variables.serverSessionID.Length; index++)
                    {
                        Variables.serverSessionID[index] = (byte)value[index];

                    }
                    Variables.Log.LogMessage(Variables.serverSessionID, "Added Server Session ID");

                    if (Variables.clientSessionID != null
                        && Utils.CompareArrays(Variables.clientSessionID, Variables.serverSessionID))
                    {
                        if (!isKeyBlockComputed)
                        {
                            Variables.Log.DisplayException("Client Key Exchange Data "
                           + "is missing in the current conversation.\n"
                           + "Please select a conversation which contains\n"
                           + "Client Key exchange message and re-launch the expert");

                            return false;
                        }
                    }
                }
                ////SSLv2 challenge Data in case Version is not SSL 2.0
                if (fieldName.Equals(filter.SslV2ChallengeData, StringComparison.OrdinalIgnoreCase))
                {
                    Variables.Log.LogMessage("Found SSLv2 Challenge Data (" + filter.SslV2ChallengeData + ")");
                    if (this.cipherSuiteInfo.SslVersion.Major != 3)
                    {
                        this.cipherSuiteInfo.SslVersion.Major = 0;
                        this.cipherSuiteInfo.SslVersion.Minor = 2;

                        Variables.Log.LogMessage("SSL Not Version 3");
                    }
                    else
                    {
                        Variables.clientIP = sourceIP;
                        Variables.serverIP = destinationIP;
                        Variables.rc4ClientEncryptedData = null;
                        Variables.clientRandomNumber = new byte[32];
                        int index = value.Length;
                        
                        for (; index < Variables.clientRandomNumber.Length; index++)
                        {
                            Variables.clientRandomNumber[index] = 
                                (byte)value[index - value.Length];
                        }
                       
                        currentState = CurrentState.ClientHelloDone;
                        Variables.Log.LogMessage(
                                            Variables.clientRandomNumber,
                                            "Client Random Number");                       
                        //// sequence number is set to Zero when ever 
                        //// there is an active state(Client Hello).
                        Variables.clientSequenceNumber = 0;
                    }
                }

                ////To parse SSL 2.0 frames
                if ((this.cipherSuiteInfo.SslVersion.Major == 0) &&
                    (this.cipherSuiteInfo.SslVersion.Minor == 2))
                {
                    Variables.Log.LogMessage("Processing SSLv2.0");
                    bool result = this.ParseSSLV2Information(
                                            fieldName,
                                            value,
                                            fieldValue,
                                            sourceIP,
                                            destinationIP);

                    if (!result)
                    {
                        Variables.Log.LogMessage("Failed to Process SSLv2.0 Data");
                        return result;
                    }

                    if (currentState == CurrentState.ApplicationData)
                    {
                        Variables.Log.LogMessage("Found Application Data, Decrypting...");
                        decryptionResult = this.DecryptApplicationData(value, sourceIP);
                        decryptedAppDataPacket = true;
                        this.currentState = CurrentState.ApplicationDataDone;
                        
                        Variables.nextFid = fid;
                        return decryptionResult;
                    }

                    if (currentState == CurrentState.ClientKeyExchange)
                    {
                        Variables.Log.LogMessage("Finished Client Key Exchange");
                        this.currentState = CurrentState.ClientKeyExchangeDone;
                        
                        Variables.nextFid = fid;
                        ////return decryptionResult;
                    }

                    continue;
                }

                if (fieldName.Equals(filter.HandshakeTypeFilter, StringComparison.OrdinalIgnoreCase)
                    && string.IsNullOrEmpty(nextfilterToSearch))
                {
                    Variables.Log.LogMessage("Found Handshake Message " + value[0] + " (" + filter.HandshakeTypeFilter + ")");
                    switch (value[0])
                    {
                        case 1:
                            Variables.Log.LogMessage("Found Client Hello Message");
                            this.currentState = CurrentState.ClientHello;
                            Variables.clientIP = sourceIP;
                            Variables.serverIP = destinationIP;
                            nextfilterToSearch = filter.ClientTimestampFilter;
                            Variables.rc4ClientEncryptedData = null;
                            break;

                        case 2:
                            if (currentState != CurrentState.ClientHelloDone)
                            {
                                if (Variables.clientRandomNumber == null)
                                {
                                    Variables.Log.DisplayException(
                                           "ClientHello message is missing");
                                }
                                else
                                {
                                    Variables.Log.DisplayException(
                                           "Simultaneous ClientHello message present");
                                    exitOnError = true;
                                    return true;
                                }

                                return false;
                            }

                            Variables.Log.LogMessage("Found Server Hello Message");

                            this.currentState = CurrentState.ServerHello;
                            nextfilterToSearch = filter.ServerTimestampFilter;
                            Variables.rc4ServerEncryptedData = null;
                            break;

                        case 16:
                            Variables.Log.LogMessage("Found Client Key Exchange Message");

                            this.currentState = CurrentState.ClientKeyExchange;
                            Variables.isSourceClient = true;
                            nextfilterToSearch = filter.ClientKeyExchangeFilter;
                            break;

                        default:
                            Variables.Log.LogMessage("Unknown Message Type Found for " + value[0]);
                            break;
                    }
                }

                if (fieldName.Equals(filter.CipherSuiteFilter, StringComparison.OrdinalIgnoreCase))
                {
                    Variables.Log.LogMessage("Found Cipher Suite Filter Message (" + filter.CipherSuiteFilter + ")");
                    if (this.currentState == CurrentState.ServerHelloDone)
                    {
                        if (fieldValue.Equals("Unknown Cipher", StringComparison.OrdinalIgnoreCase))
                        {
                            Variables.Log.DisplayException("Unknown or Unsupported Cipher Suite");
                            return false;
                        }

                        int cipherSuite = Convert.ToInt32(
                                                fieldValue.Substring(fieldValue.Length - 4, 2),
                                                16);

                        if (Enum.IsDefined(typeof(CipherSuite), cipherSuite))
                        {
                            Variables.selectedCipherSuite = (CipherSuite)cipherSuite;

                            Variables.Log.LogMessage("Using Cipher Suite: " + Variables.selectedCipherSuite.ToString());

                            ////Fill Cipher Suite Information.
                            this.FillSelectedCipherSuiteInfo();
                        }
                        else
                        {
                            Variables.Log.DisplayException(
                                    "Unknown or Unsupported Cipher Suite :"
                                    + cipherSuite.ToString());

                            return false;
                        }
                    }                    
                }

                if (fieldName.Equals(filter.ChangeCipherDataFilter, StringComparison.OrdinalIgnoreCase))
                {                
                    bool result = false;
                    Variables.Log.LogMessage("Found Change Cipher Data Filter Message (" + filter.ChangeCipherDataFilter + ")");

                    if (this.currentState == CurrentState.ServerHelloDone)
                    {
                        Variables.Log.LogMessage("Computing Keys");
                        byte[] noKeyExchangeData = new byte[0];
                        Variables.isClientKeyExchange = false;
                        result = this.ComputeKeys(
                                        Variables.isClientKeyExchange,
                                        ref isKeyBlockComputed,
                                        noKeyExchangeData,
                                        this.cipherSuiteInfo);

                        if (!result)
                        {
                            Variables.Log.LogMessage("Error: Could Not Compute Keys");
                            return false;
                        }
                    }

                    if (this.currentState != CurrentState.ClientKeyExchange)
                    {
                        if (this.ParsedFrameFieldValue(parsedFrame, fid - 2)[0] == value.Length)
                        {
                            Variables.Log.LogMessage("Decrypting Application Data");
                            result = this.DecryptApplicationData(value, sourceIP);
                            Variables.Log.LogMessage(
                                            Variables.decryptedApplicationData,
                                            this.currentState.ToString() + " Decrypted Data");
                        }
                        else
                        {
                            Variables.Log.LogMessage("Application Data is "
                                + "not reassembled properly");
                        }                        
                        
                        decryptedAppDataPacket = false;
                        this.currentState = CurrentState.ChangeCipher;
                        if (fid < fieldCount)
                        {
                            Variables.Log.LogMessage("Changing nextFid (" + Variables.nextFid + ") to " + fid + ", another message???");
                            Variables.nextFid = fid;
                            return result;
                        }                        
                    }
                }

                if (fieldName.Equals(filter.ApplicationDataFilter, StringComparison.OrdinalIgnoreCase))
                {
                    Variables.Log.LogMessage("Found Application Data Filter (" + filter.ApplicationDataFilter + ")");
                    if (Variables.serverRandomNumber == null)
                    {
                        Variables.Log.DisplayException(
                                    "Client hello and Server Hello Missing");

                        return false;
                    }

                    this.currentState = CurrentState.ApplicationData;
                }

                if (cipherSuiteInfo.DataEncryption == DataEncryptionMethod.NULL)
                {
                    //Variables.Log.LogMessage("Data Encryption Method == NULL");
                    if (fieldName == filter.NullCipherFinishedFilter)
                    {
                        Variables.Log.LogMessage("Verify Mac for Finished message...");
                        bool result = this.DecryptApplicationData(
                                                        value,
                                                        sourceIP);
                        return result;
                    }
                }

                if (currentState == CurrentState.ApplicationData)
                {
                    if (fieldName.Equals(filter.EncryptedAppDataFilter, StringComparison.OrdinalIgnoreCase))
                    {
                        Variables.Log.LogMessage("Found Encrypted Application Data (" + filter.EncryptedAppDataFilter + ")");
                        if (cipherSuiteInfo.DataEncryption != DataEncryptionMethod.NULL)
                        {
                            if (this.ParsedFrameFieldValue(parsedFrame, fid - 2)[0] == value.Length)
                            {
                                decryptionResult = this.DecryptApplicationData(value, sourceIP);
                            }
                            else
                            {
                                Variables.Log.LogMessage("ERROR!! Application Data is "
                                    + "not reassembled properly.");
                                Variables.Log.LogMessage("Length of Application Data doens't match length specified.");
                            }
                        }
                        else
                        {
                            decryptionResult = this.DecryptApplicationData(value, sourceIP);
                        }

                        decryptedAppDataPacket = true;
                        this.currentState = CurrentState.ApplicationDataDone;                        
                    }
                    else if (fieldName.Equals(filter.LdapApplicationData, StringComparison.OrdinalIgnoreCase))
                    {
                        Variables.Log.LogMessage("Found LDAP Application Data (" + filter.LdapApplicationData + ")");
                        if (cipherSuiteInfo.DataEncryption != DataEncryptionMethod.NULL)
                        {
                            if (this.ParsedFrameFieldValue(parsedFrame, fid - 3)[0] == value.Length)
                            {
                                decryptionResult = this.DecryptApplicationData(value, sourceIP);
                            }
                            else
                            {
                                Variables.Log.LogMessage("ERROR!! Application Data is "
                                    + "not reassembled properly");
                                Variables.Log.LogMessage("Length of Application Data doens't match length specified.");
                            }
                        }
                        else
                        {
                            decryptionResult = this.DecryptApplicationData(value, sourceIP);
                        }

                        decryptedAppDataPacket = true;
                        this.currentState = CurrentState.ApplicationDataDone;
                    }
                }

                if (fieldName.Equals(nextfilterToSearch, StringComparison.OrdinalIgnoreCase))
                {
                    byte[] timeStamp = new byte[4];
                    Variables.Log.LogMessage("Found Next Filter for Field: " + nextfilterToSearch);
                    if (this.currentState == CurrentState.ClientHello)
                    {
                        Variables.Log.LogMessage("Processing Client Hello");
                        this.ProcessClientHello(
                                        value,
                                        ref timeStamp,
                                        ref this.currentState,
                                        ref nextfilterToSearch,
                                        filter);

                        if (this.currentState == CurrentState.ClientHelloDone)
                        {
                            Variables.Log.LogMessage(
                                            Variables.clientRandomNumber,
                                            "Client Random Number");
                        }

                        //// sequence number is set to Zero when ever 
                        //// there is an active state(Client Hello).
                        Variables.clientSequenceNumber = 0;
                    }

                    if (this.currentState == CurrentState.ServerHello)
                    {
                        Variables.Log.LogMessage("Processing Server Hello");
                        this.ProcessServerHello(
                                    value,
                                    ref timeStamp,
                                    ref this.currentState,
                                    ref nextfilterToSearch,
                                    filter);

                        if (this.currentState == CurrentState.ServerHelloDone)
                        {
                            Variables.Log.LogMessage(
                                            Variables.serverRandomNumber,
                                            "Server Random Number");
                        }
                        //// sequence number is set to Zero when ever 
                        //// there is an active state(Server Hello).
                        Variables.serverSequenceNumber = 0;
                    }

                    if (this.currentState == CurrentState.ClientKeyExchange)
                    {
                        Variables.Log.LogMessage("Processing Client Key Exchange");
                        byte[] keyExchangeData = this.ProcessClientKeyExchange(value);
                        bool result = false;

                        if (this.CalculateKeyBlockLength())
                        {
                            Variables.isClientKeyExchange = true;

                            if (this.ComputeKeys(
                                            Variables.isClientKeyExchange,
                                            ref isKeyBlockComputed,
                                            keyExchangeData,
                                            this.cipherSuiteInfo))
                            {
                                this.currentState = CurrentState.ClientKeyExchangeDone;

                                if (fid < fieldCount)
                                {
                                    Variables.nextFid = fid;
                                    result = true;
                                }
                            }
                            else
                            {
                                //Variables.Log.LogMessage("Error: Could Not Compute Keys??");
                                Variables.Log.DisplayException("Error: Could Not Compute Keys??");
                            }
                        }

                        return result;
                    }
                }
            }

            return decryptionResult;
        }

        #endregion

        #region ParseSSLV2 Information

        /// <summary>
        /// Method to Parse SSL 2.0 Capture file
        /// </summary>
        /// <param name="fieldName">Name of the field as shown in Netmon</param>
        /// <param name="value">Value of the field</param>
        /// <param name="fieldValue">Field Value in string format</param>
        /// <param name="sourceIP">Source IP Address</param>
        /// <param name="destinationIP">Destination IP Address</param>
        /// <returns>Returns TRUE if information is parsed properly</returns>
        private bool ParseSSLV2Information(
                                    string fieldName,
                                    int[] value,
                                    string fieldValue,
                                    string sourceIP,
                                    string destinationIP)
        {
            Variables.Log.LogMessage("Entered to Process SSLv2 Info");
            if (fieldName.Equals(filter.SslV2ChallengeData, StringComparison.OrdinalIgnoreCase))
            {
                Variables.Log.LogMessage("Processing Challenge Data (" + filter.SslV2ChallengeData + ")");
                Variables.clientSequenceNumber++;
                Variables.sslv2ChallengeData = new byte[value.Length];
                for (int i = 0; i < value.Length; i++)
                {
                    Variables.sslv2ChallengeData[i] = (byte)value[i];
                }

                currentState = CurrentState.ClientHelloDone;
                Variables.clientIP = sourceIP;
                Variables.serverIP = destinationIP;
            }

            if (fieldName.Equals(filter.SslV2cipherSuiteFilter, StringComparison.OrdinalIgnoreCase))
            {
                Variables.Log.LogMessage("Processing Cipher Suite (" + filter.SslV2cipherSuiteFilter + ")");
                if (this.currentState == CurrentState.ServerHelloDone)
                {
                    int cipherSuite = Convert.ToInt32(
                                            fieldValue.Substring(fieldValue.Length - 6, 5),
                                            16);

                    if (Enum.IsDefined(typeof(CipherSuite), cipherSuite))
                    {
                        Variables.selectedCipherSuite = (CipherSuite)cipherSuite;

                        Variables.Log.LogMessage("Using Cipher Suite Data " + Variables.selectedCipherSuite);
                        ////Fill Cipher Suite Information.
                        this.FillSelectedCipherSuiteInfo();
                    }
                    else
                    {
                        Variables.Log.DisplayException(
                                "Unknown or Unsupported Cipher Suite :"
                                + cipherSuite.ToString());

                        return false;
                    }
                }
            }

            if (fieldName.Equals(filter.SslV2connectionId, StringComparison.OrdinalIgnoreCase))
            {
                Variables.Log.LogMessage("Processing Connection ID (" + filter.SslV2connectionId + ")");
                Variables.sslv2ConnectionId = new byte[value.Length];
                for (int i = 0; i < value.Length; i++)
                {
                    Variables.sslv2ConnectionId[i] = (byte)value[i];
                }

                Variables.serverSequenceNumber++;
                currentState = CurrentState.ServerHelloDone;
            }

            if (fieldName.Equals(filter.SslV2ClientMasterKey, StringComparison.OrdinalIgnoreCase))
            {
                Variables.Log.LogMessage("Processing Client Master Key (" + filter.SslV2ClientMasterKey + ")");
                currentState = CurrentState.ClientKeyExchange;
                Variables.isClientKeyExchange = true;

                Variables.clientSequenceNumber++;

                Variables.sslv2clientKeyExchange = new byte[value.Length];
                for (int i = 0; i < value.Length; i++)
                {
                    Variables.sslv2clientKeyExchange[i] = (byte)value[i];
                }

                Variables.rc4ClientEncryptedData = null;
                Variables.rc4ServerEncryptedData = null;
                Variables.clientSequenceNumber = 2;
                Variables.serverSequenceNumber = 1;

                bool result = AppDataDecryption.PreMasterKeyForRSA(
                                                    this.certificateFilePath,
                                                    this.certificatePassword,
                                                    Variables.sslv2clientKeyExchange);
                return result;
            }

            if (fieldName.Equals(filter.SslV2KeyArgument, StringComparison.OrdinalIgnoreCase))
            {
                Variables.Log.LogMessage("Processing Key Argument (" + filter.SslV2KeyArgument + ")");
                Variables.clientCipherDataIV = new byte[value.Length];
                Variables.serverCipherDataIV = new byte[value.Length];

                for (int i = 0; i < value.Length; i++)
                {
                    Variables.clientCipherDataIV[i] = (byte)value[i];
                    Variables.serverCipherDataIV[i] = (byte)value[i];
                }
            }

            if (fieldName.Equals(filter.Sslv2PaddingValue, StringComparison.OrdinalIgnoreCase))
            {
                Variables.Log.LogMessage("Found Padding Value (" + filter.Sslv2PaddingValue + ")");
                Variables.sslv2Padding = (byte)value[0];
            }

            if (fieldName.Equals(filter.SslV2EncryptedData, StringComparison.OrdinalIgnoreCase)
                || fieldName.Equals(filter.SSLv2LdapApplicationData, StringComparison.OrdinalIgnoreCase))
            {
                Variables.Log.LogMessage("Found Encrypted Data or Ldap Data (" + filter.SslV2EncryptedData + ", " + filter.SSLv2LdapApplicationData + ")");
                Variables.encryptedApplicationData = new byte[value.Length];
                for (int i = 0; i < value.Length; i++)
                {
                    Variables.encryptedApplicationData[i] = (byte)value[i];
                }

                currentState = CurrentState.ApplicationData;
            }
            
            return true;
        }

        #endregion

        #region Decrypt Application Data

        /// <summary>
        /// Decrypts the Application Data
        /// </summary>
        /// <param name="value">
        /// Array to decrypt.
        /// </param>
        /// <param name="sourceIP">
        /// Source IP Address
        /// </param>
        /// <returns>
        /// Returns TRUE is decryption is successful.
        /// </returns>
        private bool DecryptApplicationData(int[] value, string sourceIP)
        {
            Variables.encryptedApplicationData = new byte[value.Length];
            Variables.decryptedApplicationData = new byte[value.Length];

            for (int i = 0; i < value.Length; i++)
            {
                Variables.encryptedApplicationData[i] = (byte)value[i];
            }

            bool result;
            
            if ((this.cipherSuiteInfo.SslVersion.Major == 0) &&
                                 (this.cipherSuiteInfo.SslVersion.Minor == 2))
            {
                result = AppDataDecryption.DecryptSslV2ApplicationData(
                                                sourceIP,
                                                this.cipherSuiteInfo);
            }
            else
            {
                result = AppDataDecryption.DecryptSslApplicationData(
                                                sourceIP,
                                                this.cipherSuiteInfo);
            }

            Variables.Log.LogMessage("Decrypted Data with result " + result);

            return result;
        }

        #endregion

        #region Initialize Decryption Variables

        /// <summary>
        /// Initializes Decryption Variables.
        /// </summary>
        /// <param name="parsedFrame">
        /// Handle of the frame to check
        /// </param>
        /// <param name="initFilterString">
        /// Netmon Filter Initialization String
        /// </param>        
        /// <param name="cipherSuiteInfo">
        /// Cipher Suite Information.
        /// </param>
        private void InitDecryptionVariables(
                            IntPtr parsedFrame,
                            ref string initFilterString,
                            ref Variables.CipherSuiteInfo cipherSuiteInfo)
        {
            uint errorValue;
            string fieldName = string.Empty;
            string fieldValue = string.Empty;

            cipherSuiteInfo.isSSLV2Selector = false;

            uint mediaType;
            errorValue = NetmonAPI.NmGetFrameMacType(parsedFrame, out mediaType);
            if (mediaType == 1 || mediaType == 0)
            {
                initFilterString = "Ethernet.";
            }
            else if (mediaType == 7)
            {
                initFilterString = "Esp.";
            }
            else if (mediaType == 6)
            {
                initFilterString = "WiFi.Payload.LLC.Snap.";
            }

            uint fieldCount;
            errorValue = NetmonAPI.NmGetFieldCount(parsedFrame, out fieldCount);
            if (errorValue != (uint)Status.ERROR_SUCCESS)
            {
                return;
            }

            for (uint fid = 0; fid < fieldCount; fid++)
            {
                bool res = this.GetFieldName(parsedFrame, fid, out fieldName);
                if (!res)
                {
                    continue;
                }

                int[] value = this.ParsedFrameFieldValue(parsedFrame, fid);

                if (mediaType == 7 && fid == 1)
                {
                    if ((value[0] & 0x60000000) == 0x60000000)
                    {
                        initFilterString = "Ipv6.Esp.Tcp.TCPPayload.TlsSslData."; // Need SSL / TLS switch for these 4 statements
                    }
                    else if ((value[0] & 0x40000000) == 0x40000000)
                    {
                        initFilterString = "Ipv4.Esp.Tcp.TCPPayload.TlsSslData."; // Switch
                    }
                }

                if (initFilterString.Equals("Ethernet.", StringComparison.OrdinalIgnoreCase)
                    && fieldName.Equals(Constants.EthernetType, StringComparison.OrdinalIgnoreCase))
                {
                    if (value[0] == 0x0800)
                    {
                        initFilterString = "Ethernet.Ipv4.";
                    }
                    else if (value[0] == 0x86DD)
                    {
                        initFilterString = "Ethernet.Ipv6.";
                    }
                }

                if (fieldName == initFilterString + "NextProtocol")
                {
                    if (value[0] == 0x32)
                    {
                        initFilterString += "Esp.";
                    }
                    else if (value[0] == 0x29)
                    {
                        initFilterString += "Ipv6."; //.Tcp.TCPPayload.TlsSslData.";
                    }
                    else if (value[0] == 0x6)
                    {
                        initFilterString += "Tcp.TCPPayload.TlsSslData."; // Switch
                    }
                }

                if (fieldName.EndsWith(".Snap.EtherType"))
                {
                    if (value[0] == 0x800)
                    {
                        initFilterString += "Ipv4.";
                    }
                    else if(value[0] == 0x86DD)
                    {
                        initFilterString += "Ipv6.";
                    }
                }

                if (fieldName == initFilterString + "Trailer.NextProtocol")
                {
                    if (value[0] == 0x6)
                    {
                        initFilterString += "Tcp.TCPPayload.TlsSslData."; // Switch
                    }
                }

                // In this case we are just going to override the built string.  Seems easier, but perhaps it's not always perfect.  But
                // not found any instances where this doesn't work.
                if (fieldName.EndsWith(".TLSSSLData") || fieldName.EndsWith(".TLSData") || fieldName.EndsWith(".SslSmtp"))
                {
                    Variables.Log.LogMessage("Proposing Init Filter String of " + fieldName);
                    initFilterString = fieldName + ".";
                }
                                
                if (fieldName.EndsWith(".Version.Major"))
                {
                    int[] val = this.ParsedFrameFieldValue(parsedFrame, fid);
                    cipherSuiteInfo.SslVersion.Major = val[0];
                }

                if (fieldName.EndsWith(".Version.Minor"))
                {
                    int[] val = this.ParsedFrameFieldValue(parsedFrame, fid);
                    cipherSuiteInfo.SslVersion.Minor = val[0];
                }

                if (fieldName.EndsWith("SSL.SslV2RecordLayer.Header.Length"))
                {
                    cipherSuiteInfo.isSSLV2Selector = true;
                }
            }

            // Select SSL vs TLS.  SslSmtp is unique in that the version is 3.1, yet still parsed as Ssl.
            if ((cipherSuiteInfo.SslVersion.Major == 3 &&
                    (cipherSuiteInfo.SslVersion.Minor == 1 ||
                     cipherSuiteInfo.SslVersion.Minor == 2)) &&
                !(initFilterString.Contains("SslSmtp") && this.currentState == CurrentState.Initial) &&
                !cipherSuiteInfo.isSSLV2Selector)
            {
                initFilterString += "Tls.";
            }
            else if (((cipherSuiteInfo.SslVersion.Major == 3) &&
                         ((cipherSuiteInfo.SslVersion.Minor == 0) ||
                            (cipherSuiteInfo.SslVersion.Minor == 1))
                            ) ||
                         (this.currentState == CurrentState.Initial && initFilterString.Contains("SslSmtp")))
            {
                initFilterString += "Ssl.";
            }
            else
            {
                Variables.Log.DisplayException("Error: Couldn't Select TLS or SSL based on Version Info");
            }

            Variables.Log.LogMessage("Using Init Filter String of " + initFilterString);

            return;
        }

        #endregion

        #region ParsedFrame FieldValue

        /// <summary>
        /// returns the field value for the frame.
        /// </summary>
        /// <param name="parsedFrame">Parsed Frame</param>
        /// <param name="fieldId">Field Number to Display</param>
        /// <returns>Integer Array</returns>
        private int[] ParsedFrameFieldValue(
                            IntPtr parsedFrame,
                            uint fieldId)
        {
            int[] returnValue = new int[1];
            NmParsedFieldInfo parsedField = new NmParsedFieldInfo();
            parsedField.Size = (ushort)Marshal.SizeOf(parsedField);

            ////Get parsed field information.
            uint errorValue = NetmonAPI.NmGetParsedFieldInfo(
                                            parsedFrame,
                                            fieldId,
                                            parsedField.Size,
                                            ref parsedField);

            if (errorValue != (uint)Status.ERROR_SUCCESS)
            {
                Variables.Log.LogMessage("Could Not Retrieve Parsed Field Info " + errorValue);
                return null;
            }

            if (parsedField.FieldBitLength > 0)
            {
                byte number8Bit = 0;
                ushort number16Bit = 0;
                uint number32Bit = 0;
                ulong number64Bit = 0;

                switch (parsedField.ValueType)
                {
                    case FieldType.VT_UI1:
                        errorValue = NetmonAPI.NmGetFieldValueNumber8Bit(
                                                parsedFrame,
                                                fieldId,
                                                out number8Bit);

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            returnValue[0] = (int)number8Bit;
                        }
                        else
                        {
                            Console.WriteLine("Error " + errorValue);
                        }

                        break;

                    case FieldType.VT_I1:
                        errorValue = NetmonAPI.NmGetFieldValueNumber8Bit(
                                                    parsedFrame,
                                                    fieldId,
                                                    out number8Bit);

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            returnValue[0] = (int)number8Bit;
                        }
                        else
                        {
                            Console.WriteLine("Error " + errorValue);
                        }

                        break;

                    case FieldType.VT_UI2:
                        errorValue = NetmonAPI.NmGetFieldValueNumber16Bit(
                                                    parsedFrame,
                                                    fieldId,
                                                    out number16Bit);

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            returnValue[0] = (int)number16Bit;
                        }
                        else
                        {
                            Console.WriteLine("Error " + errorValue);
                        }

                        break;

                    case FieldType.VT_I2:
                        errorValue = NetmonAPI.NmGetFieldValueNumber16Bit(
                                                    parsedFrame,
                                                    fieldId,
                                                    out number16Bit);

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            returnValue[0] = (int)number16Bit;
                        }
                        else
                        {
                            Console.WriteLine("Error " + errorValue);
                        }

                        break;

                    case FieldType.VT_UI4:
                        errorValue = NetmonAPI.NmGetFieldValueNumber32Bit(
                                                    parsedFrame,
                                                    fieldId,
                                                    out number32Bit);

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            returnValue[0] = (int)number32Bit;
                        }
                        else
                        {
                            Console.WriteLine("Error " + errorValue);
                        }

                        break;

                    case FieldType.VT_I4:
                        errorValue = NetmonAPI.NmGetFieldValueNumber32Bit(
                                                parsedFrame,
                                                fieldId,
                                                out number32Bit);

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            returnValue[0] = (int)number32Bit;
                        }
                        else
                        {
                            Console.WriteLine("Error " + errorValue);
                        }

                        break;

                    case FieldType.VT_UI8:
                        errorValue = NetmonAPI.NmGetFieldValueNumber64Bit(
                                                    parsedFrame,
                                                    fieldId,
                                                    out number64Bit);

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            ////returnValue[0] = (int)number64Bit;
                            byte[] number64Array = BitConverter.GetBytes(number64Bit);
                            Array.Reverse(number64Array);
                            returnValue = new int[number64Array.Length];
                            for (int i = 0; i < number64Array.Length; i++)
                            {
                                returnValue[i] = number64Array[i];
                            }
                        }
                        else
                        {
                            Console.WriteLine("Error " + errorValue);
                        }

                        break;

                    case FieldType.VT_I8:
                        errorValue = NetmonAPI.NmGetFieldValueNumber64Bit(
                                                    parsedFrame,
                                                    fieldId,
                                                    out number64Bit);

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            returnValue[0] = (int)number64Bit;
                        }
                        else
                        {
                            Console.WriteLine("Error " + errorValue);
                        }

                        break;
                    case FieldType.VT_ARRAY | FieldType.VT_UI1:
                        byte[] byteArray = new byte[Constants.BufferSize];
                        unsafe
                        {
                            fixed (byte* barr = byteArray)
                            {
                                errorValue = NetmonAPI.NmGetFieldValueByteArray(
                                                parsedFrame,
                                                fieldId,
                                                Constants.BufferSize,
                                                barr,
                                                out number32Bit);
                            }
                        }

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            returnValue = new int[number32Bit];

                            for (int i = 0; i < number32Bit; i++)
                            {
                                returnValue[i] = Convert.ToUInt16(byteArray[i].ToString());
                            }

                            for (uint i = 0; i < number32Bit; i++)
                            {
                                ////Console.Write(byteArray[i].ToString("X2") + " ");
                            }

                            if ((parsedField.FieldBitLength >> 3) > number32Bit)
                            {
                                ////Console.Write(" ... " + ((parsedField.FieldBitLength >> 3)
                                ////- number32Bit) + " more bytes not displayed");
                            }
                        }
                        else if (errorValue == (uint)Status.ERROR_RESOURCE_NOT_AVAILABLE)
                        {
                            Console.WriteLine("The field is a container");
                        }

                        break;

                    case FieldType.VT_LPWSTR:
                        char[] name = new char[Constants.BufferSize];
                        unsafe
                        {
                            fixed (char* pstr = name)
                            {
                                errorValue = NetmonAPI.NmGetFieldValueString(
                                                parsedFrame,
                                                fieldId,
                                                Constants.BufferSize,
                                                pstr);
                            }
                        }

                        if (errorValue == (uint)Status.ERROR_SUCCESS)
                        {
                            ////Console.WriteLine(new string(name).Replace("\0", string.Empty));
                        }
                        else
                        {
                            Console.WriteLine("String is too long to display");
                        }

                        break;

                    case FieldType.VT_LPSTR:
                        ////Console.WriteLine("Should not occur");
                        break;

                    case FieldType.VT_EMPTY:
                        ////Console.WriteLine("Struct or Array types expect description");
                        break;

                    default:
                        ////Console.WriteLine("Unknown Type " + parsedField.ValueType);
                        break;
                }
            }
            else
            {
                ////Console.WriteLine("Empty");
            }

            return returnValue;
        }

        #endregion

        #region Get Field Name

        /// <summary>
        /// Retieves Field Name.
        /// </summary>
        /// <param name="parsedFrame">
        /// Handle of the frame.
        /// </param>
        /// <param name="fieldId">Id of the Field</param>
        /// <param name="fieldName">Field Name</param>
        /// <returns>Returns TRUE if Field Name is properly retrieved.</returns>
        private bool GetFieldName(
                        IntPtr parsedFrame,
                        uint fieldId,
                        out string fieldName)
        {
            char[] name = new char[Constants.BufferSize * 2];
            uint errorValue;

            unsafe
            {
                fixed (char* pstr = name)
                {
                    errorValue = NetmonAPI.NmGetFieldName(
                                                parsedFrame,
                                                fieldId,
                                                NmParsedFieldNames.NamePath,
                                                Constants.BufferSize * 2,
                                                pstr);
                }
            }

            if (errorValue == (uint)Status.ERROR_SUCCESS)
            {
                fieldName = new string(name).Replace("\0", string.Empty);
            }
            else
            {
                fieldName = string.Empty;
                return false;
            }

            return true;
        }

        #endregion

        #region Get Field Value From Netmon UI

        /// <summary>
        /// Retrieves field value shown in Netmon UI
        /// </summary>
        /// <param name="parsedFrame">Frame Handle</param>
        /// <param name="fieldId">Id of the Field</param>
        /// <param name="fieldValue">Value of the Field</param>
        /// <returns>Returns TRUE if value is properly retrieved.</returns>
        private bool GetFieldValueFromUI(
                                IntPtr parsedFrame,
                                uint fieldId,
                                out string fieldValue)
        {
            // Gets Field Value as displayed in Netmon UI
            char[] name = new char[Constants.BufferSize];
            uint errorValue;

            unsafe
            {
                fixed (char* pstr = name)
                {
                    errorValue = NetmonAPI.NmGetFieldName(
                                                parsedFrame,
                                                fieldId,
                                                NmParsedFieldNames.FieldDisplayString,
                                                Constants.BufferSize,
                                                pstr);
                }
            }

            if (errorValue == (uint)Status.ERROR_SUCCESS)
            {
                fieldValue = new string(name).Replace("\0", string.Empty);
            }
            else if (errorValue == (uint)Status.ERROR_NOT_FOUND)
            {
                int[] value = this.ParsedFrameFieldValue(parsedFrame, fieldId);
                fieldValue = string.Empty;
            }
            else
            {
                Variables.Log.LogMessage("Error Retrieving Value, NmGetFieldName Returned: " + errorValue);

                fieldValue = string.Empty;
                return false;
            }

            return true;
        }

        #endregion        
    }
}